#pragma once

#include<iostream>
#include<string>
#include<unistd.h>
#include<sys/socket.h>//套接字基础 API
#include<sys/types.h>//系统基本数据类型
#include<netinet/in.h>//IPv4
#include<arpa/inet.h>//网络地址转换函数
#include<cstdlib>
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"

//TCP 套接字实现
namespace SocketModule
{
	using namespace LogModule;
	const static int gbacklog = 16;
	// 模版方法模式
    // 纯虚函数和抽象基类,实现多态
	//定义一个包含纯虚函数的基类时，它就成为了一个抽象类，不能被实例化，
	//只能作为接口被继承。子类必须实现所有纯虚函数，才能成为一个具体类被实例化
	class Socket
	{
	public:
		virtual ~Socket() {}
        virtual void SocketOrDie() = 0;
        virtual void BindOrDie(uint16_t port) = 0;
        virtual void ListenOrDie(int backlog) = 0;
		virtual std::shared_ptr<Socket> Accept(InetAddr *client) = 0;
		virtual void Close() = 0;
        virtual int Recv(std::string *out) = 0;
        virtual int Send(const std::string &message) = 0;
        virtual int Connect(const std::string &server_ip, uint16_t port) = 0;
        virtual int Fd() = 0;

	public:
	//TCP 服务器套接字的模板构建方法
		void BuildTcpSocketMethod(uint16_t port, int backlog = gbacklog)
		{
			SocketOrDie();//创建套接字
			BindOrDie(port);//绑定地址端口
			ListenOrDie(backlog);//开始监听
		}

	//TCP 客户端套接字的简化构建方法
	//客户端通常只需创建套接字，无需提前绑定（操作系统会自动分配临时端口），
	//连接操作则延迟到与服务器建立通信时执行
		void BuildTcpClientSocketMethod()
        {
            SocketOrDie();
        }
	};

	const static int defaultfd = -1;

	class TcpSocket : public Socket
	{
	public:
		TcpSocket() : _sockfd(defaultfd)
		{
		}
		TcpSocket(int fd) : _sockfd(fd)
		{
		}
		~TcpSocket(){}

		//TCP 套接字的创建与配置
		void SocketOrDie() override
		{//指定使用 IPv4 协议族,指定使用面向连接的 TCP 协议,使用默认协议
			_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
			if(_sockfd < 0)
			{
				LOG(LogLevel::FATAL) << "socket error";
				exit(SOCKET_ERR);
			}
			//设置地址重用选项
			int opt = 1;
			//当启用SO_REUSEADDR选项后，即便对应的地址已经存在处于TIME_WAIT状态的连接，
			//套接字依然能够绑定到这个地址上。
			setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
			LOG(LogLevel::INFO) << "socket success: " << _sockfd;
		}

		//套接字地址绑定的实现
		void BindOrDie(uint16_t port) override
		{
			InetAddr localaddr(port);
			int n = ::bind(_sockfd, localaddr.NetAddrPtr(), localaddr.NetAddrLen());
			if(n < 0)
			{
				LOG(LogLevel::FATAL) << "bind error";
                exit(BIND_ERR);
			}
			LOG(LogLevel::INFO) << "bind success";
		}

		//TCP 监听队列的设置
		void ListenOrDie(int backlog) override
		{
			int n = ::listen(_sockfd, backlog);
			if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen error";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "listen success";
		}

		//TCP 连接的接受与管理
		//将底层的 accept() 系统调用封装为面向对象的接口，
		//同时处理了连接信息获取和新套接字创建的过程
		std::shared_ptr<Socket> Accept(InetAddr* client) override
		{
			struct sockaddr_in peer;
			socklen_t len = sizeof(peer);
			//接受客户端连接
			int fd = ::accept(_sockfd, CONV(peer), &len);
			//调用系统函数 accept() 从监听队列中取出一个已完成的连接
			//_sockfd 是监听套接字，负责接收新连接请求
			//peer 结构体存储客户端的地址信息（IP 和端口）
			if (fd < 0)
            {
                LOG(LogLevel::WARNING) << "accept warning ...";
                return nullptr; // TODO
            }
			//获取客户端地址信息
			//将客户端地址信息（struct sockaddr_in）传递给 InetAddr 对象
            //InetAddr::SetAddr()负责将网络字节序的地址转换为主机字节序
			client->SetAddr(peer);
            return std::make_shared<TcpSocket>(fd);
		}

		//TCP 数据接收的实现（还有点问题）
		int Recv(std::string* out) override
		{
			// 流式读取，不关心读到的是什么
			char buffer[4096*2];
			//预留一个字节用于字符串终止符（'\0'）
			ssize_t n = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
			if(n > 0)
			{
				//将接收到的数据作为 C 风格字符串处理（添加终止符）
				buffer[n] = 0;
				//将数据追加到输出字符串
				*out += buffer;
			}
			//返回实际读取的字节数（与 read() 行为一致）
			return n;
		}

		//TCP 数据发送的实现
		int Send(const std::string &message) override
		{
			return send(_sockfd, message.c_str(), message.size(), 0);
		}

		//套接字关闭
		void Close() override //??
        {
            if (_sockfd >= 0)
                ::close(_sockfd);
        }

        int Connect(const std::string &server_ip, uint16_t port) override
        {
            InetAddr server(server_ip, port);
            return ::connect(_sockfd, server.NetAddrPtr(), server.NetAddrLen());
        }

        int Fd()
        {
            return _sockfd;
        }
	private:
		int _sockfd;	
	};
}